library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity execStage is
  port (
    clk            : in std_logic;
    nReset         : in std_logic;
    holdStage      : in std_logic;
    invalidate     : in std_logic;
    nPC_in         : in std_logic_vector(31 downto 0);
    instruction_in : in operation;
    busA_in        : in std_logic_vector(31 downto 0);
    busB_in        : in std_logic_vector(31 downto 0);

    rs_in : in std_logic_vector(4 downto 0);
    rt_in : in std_logic_vector(4 downto 0);


    immediate_in   : in  std_logic_vector(31 downto 0);
    upper_in       : in  std_logic_vector(31 downto 0);
    shftamnt_in    : in  std_logic_vector(4 downto 0);
    destination_in : in  std_logic_vector(4 downto 0);
    destination    : out std_logic_vector(4 downto 0);
    rt             : out std_logic_vector(4 downto 0);
    aluResult      : out std_logic_vector(31 downto 0);
    busB           : out std_logic_vector(31 downto 0);
    upper          : out std_logic_vector(31 downto 0);
    instruction    : out operation;

    forwardIn  : in  execForwardIn;
    forwardOut : out execForwardOut;

    nPC : out std_logic_vector(31 downto 0));
end execStage;

architecture execStage_arch of execStage is


  component alu
    port (opcode   : in  aluOp;
          uns      : in  std_logic;
          A, B     : in  std_logic_vector (31 downto 0);
          output   : out std_logic_vector (31 downto 0);
          negative : out std_logic;
          overflow : out std_logic;
          zero     : out std_logic);
  end component;

  component execInterstageReg
    port (
      clk            : in std_logic;
      nReset         : in std_logic;
      holdStage      : in std_logic;
      invalidate     : in std_logic;
      nPC_in         : in std_logic_vector(31 downto 0);
      upper_in       : in std_logic_vector(31 downto 0);
      aluResult_in   : in std_logic_vector(31 downto 0);
      busB_in        : in std_logic_vector(31 downto 0);
      destination_in : in std_logic_vector(4 downto 0);
      rt_in          : in std_logic_vector(4 downto 0);
      instruction_in : in operation;

      upper       : out std_logic_vector(31 downto 0);
      aluResult   : out std_logic_vector(31 downto 0);
      busB        : out std_logic_vector(31 downto 0);
      destination : out std_logic_vector(4 downto 0);
      rt          : out std_logic_vector(4 downto 0);
      instruction : out operation;
      nPC         : out std_logic_vector(31 downto 0));
  end component;

  signal uns                        : std_logic;
  signal op                         : operation;
  signal alu_opcode                 : aluOp;
  signal t_aluResult, f_aluResult, aluSrc_result : std_logic_vector(31 downto 0);
  signal fullShftamnt, A, B         : std_logic_vector(31 downto 0);
  
  
begin  -- fetchInterstageReg  

  op <= instruction_in;

  forwardOut.destination <= destination_in;
  forwardOut.rs <= rs_in;
  forwardOut.rt <= rt_in;

  --just leave our flages hanging since they aren't useful really.
  alu_c : alu port map (
    opcode => alu_opcode,
    uns    => uns,
    A      => A,
    B      => aluSrc_result,
    output => t_aluResult);

  inter_c : execInterstageReg port map (
    clk            => clk,
    nReset         => nReset,
    holdStage      => holdStage,
    invalidate     => invalidate,
    nPC_in         => nPC_in,
    upper_in       => upper_in,
    aluResult_in   => f_aluResult,
    busB_in        => B,
    destination_in => destination_in,
    instruction_in => instruction_in,
    rt_in          => rt_in,
    upper          => upper,
    aluResult      => aluResult,
    busB           => busB,
    destination    => destination,
    instruction    => instruction,
    rt             => rt,
    nPC            => nPC);     

  fullShftamnt(31 downto 5) <= (others => '0');
  fullShftamnt(4 downto 0)  <= shftamnt_in;
  unsign : process (op)
  begin  -- process unsign
    case op is
      when o_sltu | o_sltiu | o_addu | o_subu | o_addiu =>
        uns <= '1';
      when others =>
        uns <= '0';
    end case;
  end process unsign;

  aluS : process (op, fullShftamnt, immediate_in, B)
  begin  -- 1 = immediate 0 = bus B
    case op is
      when o_sll | o_srl =>
        aluSrc_result <= fullShftamnt;
      when o_addiu | o_andi | o_ori | o_slti | o_sltiu | o_xori | o_sw | o_ll | o_lw | o_sc =>
        aluSrc_result <= immediate_in;
      when others =>
        aluSrc_result <= B;
    end case;
  end process aluS;


  forwA : process (busA_in, forwardIn)
  begin  -- process t_rdat1
    case forwardIn.selA is
      when '1' =>
        A <= forwardIn.busA;
      when others =>
        A <= busA_in;
    end case;
  end process forwA;

  forwB : process (busB_in, forwardIn)
  begin  -- process t_rdat1
    case forwardIn.selB is
      when '1' =>
        B <= forwardIn.busB;
      when others =>
        B <= busB_in;
    end case;
  end process forwB;


  toReg : process (op, upper_in, nPC_in, t_aluResult)
  begin  -- process memToReg 0=ALU 1=DataMem 2=shifted imm 3=PC+4?
    case op is
      when o_lui =>
       f_aluResult <= upper_in;
      when o_jal =>
        f_aluResult <= nPC_in;
      when others =>                    --rt
        f_aluResult <= t_aluResult;
    end case;
  end process toReg;

  aluO : process (op)
  begin  -- process alu
    case op is
      when o_sltu | o_sltiu | o_slt | o_slti =>
        alu_opcode <= op_slt;
      when o_sll =>
        alu_opcode <= op_sll;
      when o_srl =>
        alu_opcode <= op_srl;
      when o_addu | o_addiu | o_lw | o_sw | o_ll | o_sc =>  -- add register plus offest
        alu_opcode <= op_add;
      when o_subu =>
        alu_opcode <= op_sub;
      when o_and | o_andi =>
        alu_opcode <= op_and;
      when o_nor =>
        alu_opcode <= op_nor;
      when o_or | o_ori =>
        alu_opcode <= op_or;
      when o_xor | o_xori =>
        alu_opcode <= op_xor;
      when others =>
        alu_opcode <= op_sll;
    end case;
  end process aluo;
  
end execStage_arch;
